package com.hdu.utils;


import com.hdu.result.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

import static java.util.Optional.ofNullable;

@Slf4j
@Component
public class MySQLExecutor {

    private final ConnectionHelper connectionHelper;

    public MySQLExecutor(ConnectionHelper connectionHelper) {
        this.connectionHelper = connectionHelper;
    }


    public <T> T queryOne(Class<T> clazz, String sql) {
        return ofNullable(queryList(clazz, sql))
                .map(list -> list.get(0))
                .orElse(null);
    }


    @SuppressWarnings("all")
    public <T> List<T> queryList(Class<T> clazz, String sql) {
        Connection connection = connectionHelper.getConnection();
        try {
            PreparedStatement ps = connection.prepareStatement(sql);
            List<T> res = new ArrayList<>();
            ResultSet resultSet = ps.executeQuery();
            Constructor<T> constructor = clazz.getConstructor();
            constructor.setAccessible(true);
            while (resultSet.next()) {
                T singleRes = constructor.newInstance();
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    String fieldName = camelToSnake(field.getName());
                    if (field.getType().equals(String.class)) {
                        field.set(singleRes, resultSet.getString(fieldName));
                    } else if (field.getType().equals(int.class)) {
                        field.set(singleRes, resultSet.getInt(fieldName));
                    } else if (field.getType().equals(long.class)) {
                        field.set(singleRes, resultSet.getLong(fieldName));
                    } else if (field.getType().equals(double.class)) {
                        field.set(singleRes, resultSet.getDouble(fieldName));
                    } else {
                        field.set(singleRes, resultSet.getString(fieldName));
                    }
                }
                res.add(singleRes);
            }
            return res;
        } catch (SQLException e) {
            log.error("query error, sql : {}", sql);
            return null;
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
                 InvocationTargetException e) {
            log.error("未知异常", e);
            return null;
        }
    }

    @SuppressWarnings("all")
    public R insert(String sql) {
        Connection connection = connectionHelper.getConnection();
        try {
            PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            int rowsAffected = ps.executeUpdate();
            if (rowsAffected > 0) {
                ResultSet generatedKeys = ps.getGeneratedKeys();
                if (generatedKeys.next()) {
                    long generatedId = generatedKeys.getLong(1);
                    return R.success(generatedId);
                } else {
                    log.error("insert error, sql : {}", sql);
                    return R.fail("insert error");
                }
            } else {
                return R.fail("insert error");
            }
        } catch (SQLException e) {
            log.error("insert error, sql : {}", sql);
            return R.fail(e.getMessage());
        }
    }

    @SuppressWarnings("all")
    public boolean delete(String sql) {
        Connection connection = connectionHelper.getConnection();
        try {
            PreparedStatement ps = connection.prepareStatement(sql);
            int rowsAffected = ps.executeUpdate();
            return rowsAffected > 0;
        } catch (SQLException e) {
            log.error("delete error, sql : {}", sql);
            return false;
        }
    }

    private String camelToSnake(String camelCaseStr) {
        return camelCaseStr.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
    }
}
